#include "iowidget.h"
#include "dm/dmdef.h"
#include "globalconfig.h"
#include "limiterwidget.h"
#include "compressorwidget.h"
#include "noisegatewidget.h"


#include <cstring>

IOWidget::IOWidget(const Type &type, const uint8_t &channel, const stDevCfg &cfg, const stDevLimt &limt, QWidget *parent)
    : AWidget{parent}
    , m_type{type}
    , m_channel{channel}
{
    QList<EQ> list;
    EQ arg;
    if (limt.tru_eq == EQ_15_FREQUENCY_SIZE) {
        for (size_t i = 0; i < EQ_15_FREQUENCY_SIZE; ++i) {
            arg.freq = EQ_15_FREQUENCY.at(i);
            list.append(arg);
            m_dmList.push_back(arg);
        }
    }
    else if (limt.tru_eq == EQ_11_FREQUENCY_SIZE) {
        for (size_t i = 0; i < EQ_11_FREQUENCY_SIZE; ++i) {
            arg.freq = EQ_11_FREQUENCY.at(i);
            list.append(arg);
            m_dmList.push_back(arg);
        }
    }
    else if (limt.tru_eq == EQ_31_FREQUENCY_SIZE) {
        for (size_t i = 0; i < EQ_31_FREQUENCY_SIZE; ++i) {
            arg.freq = EQ_31_FREQUENCY.at(i);
            list.append(arg);
            m_dmList.push_back(arg);
        }
    }
    else {
        for (size_t i = 0, size = limt.tru_eq; i < size; ++i) {
            list.append(arg);
            m_dmList.push_back(arg);
        }
    }

    gain = new GainWidget(channel, limt.max_gain, this);
    delay = new DelayWidget(channel, cfg.sample, limt.max_dly, this);

    ucs = new FreqGainUCSWidget(this);
    xoverWidget = new XoverWidget(this);
    eqOp = new EqOpWidget(list, this);
    dynamicX = new DynamicXWidget(limt.max_thr, this);

    m_dmList.push_back(xoverWidget->hpfValue());
    m_dmList.push_back(xoverWidget->lpfValue());

    ucs->setList(m_dmList);

    auto layout = new QVBoxLayout(this);
    auto hLayout = new QHBoxLayout;

    layout->addWidget(ucs, 6);
    layout->addWidget(new AHLine(this));
    layout->addLayout(hLayout, 4);
    layout->setSpacing(10 * ratio());

    hLayout->setContentsMargins(0, 0, 0, 0);
    hLayout->setSpacing(30 * ratio());

    hLayout->addWidget(gain);
    hLayout->addWidget(delay);
    hLayout->addWidget(xoverWidget);
    hLayout->addWidget(eqOp, 0, Qt::AlignHCenter);
    hLayout->addWidget(dynamicX, 0, Qt::AlignHCenter);

    if (type == Input) {
        dynamicX->newTab(DynamicX::NoiseGate);
        if (cfg.limt & 0x01) {
            dynamicX->newTab(DynamicX::Limiter);
        }
        if (cfg.cmps & 0x01) {
            dynamicX->newTab(DynamicX::Compressor);
        }
    }
    else if (type == Output) {
        if (cfg.limt & 0x02) {
            dynamicX->newTab(DynamicX::Limiter);
        }
        if (cfg.cmps & 0x02) {
            dynamicX->newTab(DynamicX::Compressor);
        }
    }
    // ui
    connect(ucs, &FreqGainUCSWidget::stateChanged, this, &IOWidget::ucsStateChanged);
    connect(eqOp, &EqOpWidget::stateChanged, ucs, &FreqGainUCSWidget::updateDm);
    // ui -> device
    // gain
    connect(gain, &GainWidget::speakerChanged, &GlobalConfig::getInstance(), &GlobalConfig::mute);
    connect(gain, &GainWidget::phaseChanged, &GlobalConfig::getInstance(), &GlobalConfig::phase);
    connect(gain, &GainWidget::gainChanged, &GlobalConfig::getInstance(), &GlobalConfig::gain);
    // delay
    connect(delay, &DelayWidget::delay, &GlobalConfig::getInstance(), &GlobalConfig::delay);
    // xover
    connect(xoverWidget, &XoverWidget::stateChanged, this, &IOWidget::xoverStateChanged);
    // eq
    connect(eqOp, &EqOpWidget::stateChanged, this, &IOWidget::eqStateChanged);
    // dynamicx
    connect(dynamicX, &DynamicXWidget::stateChanged, this, &IOWidget::dynamicXStateChanged);
    // transmit
    connect(this, &IOWidget::xover, &GlobalConfig::getInstance(), &GlobalConfig::xover);
    connect(this, &IOWidget::peq, &GlobalConfig::getInstance(), &GlobalConfig::peq);
    connect(this, &IOWidget::compressor, &GlobalConfig::getInstance(), &GlobalConfig::compressor);
    connect(this, &IOWidget::limiter, &GlobalConfig::getInstance(), &GlobalConfig::limiter);
    connect(this, &IOWidget::noiseGate, &GlobalConfig::getInstance(), &GlobalConfig::noiseGate);
}

IOWidget::Type IOWidget::type() const
{
    return m_type;
}

uint8_t IOWidget::channel() const
{
    return m_channel;
}

void IOWidget::set(const PARAMIO &param)
{
    std::visit(
        overloaded{
            [this](const PARAM_MUS &mus) {
                set(mus, GlobalConfig::getInstance().cfgDev().mus.tru_eq);
                for (int i = 0; i < dynamicX->count(); ++i) {
                    if (auto w = qobject_cast<NoiseGateWidget*>(dynamicX->widget(i))) {
                        w->set(NoiseGate{mus.nsgt.threshold + NOISE_GATE_THRESHOLD_MIN});
                    }
                    else if (auto w = qobject_cast<CompressorWidget*>(dynamicX->widget(i))) {
                        w->set(Compressor{mus.cmps.threshold * DYNAMIC_X_THRESHOLD_STEP + DYNAMIC_X_THRESHOLD_MIN, mus.cmps.attack, mus.cmps.decay, mus.cmps.ratio});
                    }
                }
            },
            [this](const PARAM_OUT &out) {
                set(out, GlobalConfig::getInstance().cfgDev().out.tru_eq);
                for (int i = 0; i < dynamicX->count(); ++i) {
                    if (auto w = qobject_cast<CompressorWidget*>(dynamicX->widget(i))) {
                        w->set(Compressor{out.cmps.threshold * DYNAMIC_X_THRESHOLD_STEP + DYNAMIC_X_THRESHOLD_MIN, out.cmps.attack, out.cmps.decay, out.cmps.ratio});
                    }
                    else if (auto w = qobject_cast<LimiterWidget*>(dynamicX->widget(i))) {
                        w->set(Limiter{out.limt.threshold * DYNAMIC_X_THRESHOLD_STEP + DYNAMIC_X_THRESHOLD_MIN, out.limt.attack, out.limt.decay});
                    }
                }
            },
        }, param);
}

void IOWidget::peak(const uint8_t &value)
{
    gain->peak(value);
}

void IOWidget::xoverStateChanged(const Xover &xo, bool init)
{
    if (xo.type == XoverType::Hpf) {
        ucs->updateDm(m_dmList.size() - 2, xo);
    }
    else {
        ucs->updateDm(m_dmList.size() - 1, xo);
    }
    if (init) {
        return;
    }
    stDataXover data;
    data.freq = xo.freq;
    data.filter = xo.ftype * (Slope::Oct_48dB + 1) + xo.slope;

    emit xover(m_channel, xo.type, data);
}

void IOWidget::eqStateChanged(const int &index, const EQ &eq, bool init)
{
    if (init) {
        return;
    }
    stDataEq data;
    data.freq = eq.freq;
    data.gain = static_cast<uint16_t>((eq.gain - EQ_GAIN_MIN) / EQ_GAIN_STEP);
    data.bw = eq.bw;
    data.type = eq.type;

    emit peq(m_channel, static_cast<uint8_t>(index), data);
}

void IOWidget::dynamicXStateChanged(const DXVariant &dx)
{
    std::visit(
        overloaded{
            [this](const NoiseGate &param) {
                stDataNsgt data;
                data.threshold = static_cast<uint16_t>(param.threshold - NOISE_GATE_THRESHOLD_MIN);

                emit noiseGate(m_channel, data);
            },
            [this](const Compressor &param) {
                stDataCmps data;
                data.threshold = static_cast<uint16_t>((param.threshold - DYNAMIC_X_THRESHOLD_MIN) / DYNAMIC_X_THRESHOLD_STEP);
                data.attack = param.attack;
                data.decay = param.decay;
                data.ratio = param.ratio;

                emit compressor(m_channel, data);
            },
            [this](const Limiter &param) {
                stDataLimt data;
                data.threshold = static_cast<uint16_t>((param.threshold - DYNAMIC_X_THRESHOLD_MIN) / DYNAMIC_X_THRESHOLD_STEP);
                data.attack = param.attack;
                data.decay = param.decay;

                emit limiter(m_channel, data);
            }
        }, dx);
}

void IOWidget::ucsStateChanged(const int &index, const DMVariant &dm)
{
    if (index == m_dmList.size() - 2 || index == m_dmList.size() - 1) {
        if (const auto param = std::get_if<Xover>(&dm)) {
            xoverWidget->set(*param, false);
        }
    }
    else if (index > -1 && index < m_dmList.size() - 2) {
        if (const auto eq = std::get_if<EQ>(&dm)) {
            eqOp->set(index, *eq, false);
        }
    }
}


template<class T>
void IOWidget::set(const T &param, int size)
{
    gain->set(param.gain, param.mute, param.phase);
    delay->set(param.delay);
    auto hpf = param.xover[0];
    auto lpf = param.xover[1];
    xoverWidget->set(Xover{XoverType::Hpf, hpf.freq, static_cast<PfType>(hpf.filter / 4), static_cast<Slope>(hpf.filter % 4)}, true);
    xoverWidget->set(Xover{XoverType::Lpf, lpf.freq, static_cast<PfType>(lpf.filter / 4), static_cast<Slope>(lpf.filter % 4)}, true);
    for (int i = 0; i < size; ++i) {
        const auto &eq = param.eq[i];
        eqOp->set(i, EQ{eq.freq, static_cast<EQType>(eq.type), eq.bw, eq.gain * EQ_GAIN_STEP + EQ_GAIN_MIN}, true);
    }
}
